package org.github.manx98.jlibhac.common.keys;

import org.github.manx98.jlibhac.boot.EncryptedKeyBlob;
import org.github.manx98.jlibhac.boot.KeyBlob;
import org.github.manx98.jlibhac.crypto.AesKey;
import org.github.manx98.jlibhac.crypto.AesXtsKey;
import org.github.manx98.jlibhac.fs.RightsId;
import org.github.manx98.jlibhac.fs.fsa.IFile;
import org.github.manx98.jlibhac.fs.fsa.IFileSystem;
import org.github.manx98.jlibhac.fs.fsa.OpenMode;
import org.github.manx98.jlibhac.spl.AccessKey;
import org.github.manx98.jlibhac.tools.crypto.RSAParameters;
import org.github.manx98.jlibhac.tools.es.Ticket;
import org.github.manx98.jlibhac.tools.es.TitleKeyType;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 密钥存储对象
 *
 * @author Manx98
 * @version 1.0
 * @since 2022/10/2 23:36
 */
public class KeySet {
    public final Map<RightsId, AccessKey> ExternalKeySet = new HashMap<>();
    public final AesKey SecureBootKey = new AesKey();
    public final AesKey TsecKey = new AesKey();
    public final AesKey Package1MacKek = new AesKey();
    public final AesKey TsecRootKek = new AesKey();
    public final AesKey DeviceKey = new AesKey();
    public final AesKey Package1Kek = new AesKey();
    public final AesKey[] TsecAuthSignatures = createArray(32);
    public final RSAParameters ETicketExtKeyRsa = new RSAParameters();

    private AesKey[] createArray(int size) {
        AesKey[] aesKeys = new AesKey[size];
        for (int i1 = 0; i1 < aesKeys.length; i1++) {
            aesKeys[i1] = new AesKey();
        }
        return aesKeys;
    }

    public final AesKey[] TsecRootKeys = createArray(32);
    public final AesKey KeyBlobMacKeySource = new AesKey();
    public final AesKey[] KeyBlobKeySources = createArray(32);
    public final AesKey[] KeyBlobKeys = createArray(32);
    public final AesKey[] KeyBlobMacKeys = createArray(32);
    public final AesKey[] MasterKekSources = createArray(32);
    public final AesKey MarikoBek = new AesKey();
    public final AesKey MarikoKek = new AesKey();
    public final AesKey[] MarikoAesClassKeys = createArray(12);
    public final AesKey[] MarikoMasterKekSources = createArray(32);
    public final AesKey[] MasterKeks = createArray(32);
    public final AesKey MasterKeySource = new AesKey();
    public final AesKey[] Package1MacKeys = createArray(32);
    public final AesKey[] Package1Keys = createArray(32);
    public final AesKey[] MasterKeys = createArray(32);
    public final AesKey Package2KeySource = new AesKey();
    public final AesKey[] Package2Keys = createArray(32);
    public final AesKey BisKekSource = new AesKey();
    public final AesXtsKey[] BisKeySources = createAesXtsKeyArray(4);

    private AesXtsKey[] createAesXtsKeyArray(int size) {
        AesXtsKey[] aesKeys = new AesXtsKey[size];
        for (int i1 = 0; i1 < aesKeys.length; i1++) {
            aesKeys[i1] = new AesXtsKey();
        }
        return aesKeys;
    }

    public final AesXtsKey[] BisKeys = createAesXtsKeyArray(4);
    public final AesKey[][] KeyAreaKeys = createKeyAreaKeys();

    private AesKey[][] createKeyAreaKeys() {
        AesKey[][] aesKeys = new AesKey[32][];
        for (int i2 = 0; i2 < 32; i2++) {
            aesKeys[i2] = createArray(3);
        }
        return aesKeys;
    }

    public final AesKey ETicketRsaKek = new AesKey();
    public final AesKey[] GcTitleKeyKeks = new AesKey[16];
    public final AesKey PerConsoleKeySource = new AesKey();
    public final AesKey RetailSpecificAesKeySource = new AesKey();
    public final AesKey AesKekGenerationSource = new AesKey();
    public final AesKey AesKeyGenerationSource = new AesKey();
    public final AesKey TitleKekSource = new AesKey();
    public final AesKey[] TitleKeks = createArray(32);
    public final AesKey HeaderKekSource = new AesKey();
    public final AesXtsKey HeaderKeySource = new AesXtsKey();
    public final AesXtsKey HeaderKey = new AesXtsKey();
    public final AesKey KeyAreaKeyApplicationSource = new AesKey();
    public final AesKey KeyAreaKeyOceanSource = new AesKey();
    public final AesKey KeyAreaKeySystemSource = new AesKey();
    public final AesKey DeviceUniqueSaveMacKekSource = new AesKey();
    public final AesKey[] DeviceUniqueSaveMacKeySources = createArray(2);
    public final AesKey[] DeviceUniqueSaveMacKeys = createArray(2);
    public final AesKey SeedUniqueSaveMacKekSource = new AesKey();
    public final AesKey SeedUniqueSaveMacKeySource = new AesKey();
    public final AesKey SeedUniqueSaveMacKey = new AesKey();
    public final AesKey SdCardEncryptionSeed = new AesKey();
    public final AesKey SdCardKekSource = new AesKey();
    public final AesXtsKey[] SdCardKeySources = createAesXtsKeyArray(3);
    public final AesKey XciHeaderKey = new AesKey();
    public final AesKey SslRsaKek = new AesKey();
    public final EncryptedKeyBlob[] EncryptedKeyBlobs = createEncryptedKeyBlobArray();

    private EncryptedKeyBlob[] createEncryptedKeyBlobArray() {
        EncryptedKeyBlob[] encryptedKeyBlobs = new EncryptedKeyBlob[32];
        for (int i1 = 0; i1 < encryptedKeyBlobs.length; i1++) {
            encryptedKeyBlobs[i1] = new EncryptedKeyBlob();
        }
        return encryptedKeyBlobs;
    }

    public KeyBlob[] KeyBlobs = createKeyBlobArray();

    private KeyBlob[] createKeyBlobArray() {
        KeyBlob[] keyBlobs = new KeyBlob[32];
        for (int i1 = 0; i1 < keyBlobs.length; i1++) {
            keyBlobs[i1] = new KeyBlob();
        }
        return keyBlobs;
    }

    public static KeySet createDefaultKeySet() {
        return new KeySet();
    }

    public void importTickets(IFileSystem fs) throws IOException {
        fs.enumerateEntries("/", ".*\\.tik", ticketEntry -> {
            try (IFile ticketFile = fs.doOpenFile(ticketEntry.getPath(), OpenMode.Read)) {
                Ticket ticket = new Ticket(ticketFile.asStorage());
                if (ticket.titleKeyType == TitleKeyType.Common) {
                    this.ExternalKeySet.put(new RightsId(ticket.RightsId), new AccessKey(ticket.getTitleKey(this)));
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return true;
        });
    }
}
